Shader "Custom/SDF"
{
    Properties
    {
        _DetailTexture ("_DetailTexture", 2D)="white" {}
    }
    
    SubShader
    {

//        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Name "SDF"
            HLSLPROGRAM

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
            #include "SDFInclude.hlsl"
            #pragma vertex vert
            #pragma fragment frag

            #define _MaxStep 50
            #define _MinDis 0.0001

            struct v2f
            {
                float2 texcoord : TEXCOORD0;
                float4 positionCS : SV_POSITION;
                float3 worldPos : TEXCOORD1;
            };
            struct SceneInfo
            {
                float4 color;
                float dis;
                float3 normal;
            };
            // 自定义数据结构
            struct ShapeData
            {
                float3 position;
                float3 size;
                float4 color;
                float4 custom;
                int shapeType;
                int operation;
                float blendStrength;
                int numChildren;
            };
            StructuredBuffer<ShapeData> _ShapeDataBuffer;
            int _ShapeDataCount;
            
            TEXTURE2D( _MainTex);
            SAMPLER(sampler_MainTex);
            TEXTURE2D( _DetailTexture);
            SAMPLER(sampler_DetailTexture);
            float _DegreeOfFuse;
            float4x4 _IVP;
            int _MaxStepValue;
            float _DistabilizationAmplitude;
            float _DistabilizationFraquence;
            float _DistabilizationSpeed;
            float CombinShapeDistance(float oldDis, float newDis)
            {
                return SmoothUnion(oldDis, newDis, _DegreeOfFuse);
                // return min(oldDis, newDis);
            }
            
            float GetShapeDistance2D(ShapeData data, float2 p)
            {
                float2 dis = p - data.position.xy;
                float ret = 9999999;

                // switch 有分支发散导致性能低下的问题
                switch (data.shapeType)
                {
                case 1:
                    ret = sdfSphereDis2D(dis, data.size.x/2.0);
                    break;
                case 2:
                    ret = sdBoxDis2D(dis, data.size/ 2);
                    break;
                case 3:
                    ret = sdEquilateralTriangleDis2D(dis, data.size.x/2);
                    break;
                }

                // 计算代替switch
                // ret = half(data.shapeType == 1) *sdfSphereDis2D(dis, data.size.x/2.0) +
                //     half(data.shapeType == 2) *sdBoxDis2D(dis, data.size/ 2) +
                //         half(data.shapeType == 3) *sdEquilateralTriangleDis2D(dis, data.size.x/2);
                
                
                ret += sin((_Time.y*_DistabilizationSpeed) + (p.x ) * _DistabilizationFraquence / data.size.x) *
                    sin((_Time.y*_DistabilizationSpeed)  + (p.y )*_DistabilizationFraquence/ data.size.y) /   
                        (_DistabilizationAmplitude /  max(0.001, (data.size.x + data.size.y) / 2));
                return ret;
            }

            float GetShapeDistance(ShapeData data, float3 p)
            {
                if (_MaxStepValue == 1)
                    p.z = 0;
                float3 dis = p - data.position;
                float ret = 99999;
                switch (data.shapeType)
                {
                case 1:
                    ret = sdfSphereDis(dis, data.size.x/2);
                    break;
                }
                ret += sin(_Time.y + (p.x ) *_DistabilizationFraquence / data.size.x) *
                    sin(_Time.y + (p.y )* _DistabilizationFraquence/ data.size.x) *
                    sin(_Time.y + (p.z )* _DistabilizationFraquence/3/ data.size.x) / _DistabilizationAmplitude * data.size.x;
                return ret;
            }
            float CalcDis(float3 p)
            {
                float dis = 111;
                for (int i=0; i < _ShapeDataCount; ++i)
                {
                    float newDis = GetShapeDistance(_ShapeDataBuffer[i], p);
                    dis = CombinShapeDistance(dis, newDis);
                }
                return dis;
            }
            float3 RebuildNormal(float3 p)
            {
                float tm = 0.001;
                float x, y , z=0;
                x = CalcDis(float3(p.x + tm, p.y, p.z)) - CalcDis(float3(p.x - tm, p.y, p.z));
                y = CalcDis(float3(p.x , p.y+ tm, p.z)) - CalcDis(float3(p.x , p.y- tm, p.z));
                z = CalcDis(float3(p.x , p.y, p.z+ tm)) - CalcDis(float3(p.x , p.y, p.z- tm));
                return normalize(float3(x, y, z));
            }
            SceneInfo GetSceneInfo3D(float3 p)
            {
                SceneInfo info ;
                if (_ShapeDataCount <= 0) 
                    return info;
                info.dis = 111;
                info.color = float4(0,0,0,0);
                
                for (int i=0; i < _ShapeDataCount; ++i)
                {
                    float newDis = GetShapeDistance(_ShapeDataBuffer[i], p);
                    info.dis = CombinShapeDistance(info.dis, newDis);
                    info.color  += (_ShapeDataBuffer[i].color * max( 1  - newDis, 0));
                }
                info.normal = RebuildNormal(p);
                return info; 
                // min(min(ss2, ss), ss2 * ss / 10)
            }
            SceneInfo GetSceneInfo2D(float3 p)
            {
                SceneInfo info ;
                if (_ShapeDataCount <= 0) 
                    return info;
                info.dis = 111;
                info.color = float4(0,0,0,0);
                info.normal = float3(0, 0, 0);

                //  为了优化，分四个
                // if (_ShapeDataCount > 4)
                // {
                //     for (int i=0; i < _ShapeDataCount-4; i+=4)
                //     {
                //         float4 disVec;
                //         disVec.x = GetShapeDistance2D(_ShapeDataBuffer[i], p);
                //         disVec.y = GetShapeDistance2D(_ShapeDataBuffer[i+1], p);
                //         disVec.z = GetShapeDistance2D(_ShapeDataBuffer[i+2], p);
                //         disVec.w = GetShapeDistance2D(_ShapeDataBuffer[i+3], p);
                //         info.dis = CombinShapeDistance(CombinShapeDistance(CombinShapeDistance(CombinShapeDistance(info.dis, disVec.x), disVec.y ), disVec.z), disVec.w);
                //         info.color  += (_ShapeDataBuffer[i].color * max( 1  - disVec.x, 0));
                //         info.color  += (_ShapeDataBuffer[i+1].color * max( 1  - disVec.y, 0));
                //         info.color  += (_ShapeDataBuffer[i+2].color * max( 1  - disVec.z, 0));
                //         info.color  += (_ShapeDataBuffer[i+3].color * max( 1  - disVec.w, 0));
                //         info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i].position.xy) * clamp( 1  -  disVec.x, 0, 1), 1);
                //         info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i+1].position.xy) * clamp( 1  -  disVec.y, 0, 1), 1);
                //         info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i+2].position.xy) * clamp( 1  -  disVec.z, 0, 1), 1);
                //         info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i+3].position.xy) * clamp( 1  -  disVec.w, 0, 1), 1);
                //     }
                // }
                // for (int i=_ShapeDataCount - (_ShapeDataCount % 4); i < _ShapeDataCount % 4; ++i)
                // {
                //     // if (_ShapeDataBuffer[i].shapeType == 0)
                //     //     continue;
                //     float newDis = GetShapeDistance2D(_ShapeDataBuffer[i], p);
                //     info.dis =CombinShapeDistance(info.dis, newDis);
                //     info.color  += (_ShapeDataBuffer[i].color * max( 1  - newDis, 0));
                //
                //     float rc =  clamp( 1  - newDis, 0, 1);
                //     info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i].position.xy) * rc, 1);
                // }
                
                for (int i=0; i < _ShapeDataCount ; ++i)
                {
                    // if (_ShapeDataBuffer[i].shapeType == 0)
                    //     continue;
                    float newDis = GetShapeDistance2D(_ShapeDataBuffer[i], p);
                    info.dis =CombinShapeDistance(info.dis, newDis);
                    info.color  += (_ShapeDataBuffer[i].color * max( 1  - newDis, 0));
                
                    float rc =  clamp( 1  - newDis, 0, 1);
                    info.normal += float3(normalize(p.xy - _ShapeDataBuffer[i].position.xy) * rc, 1);
                }
                info.normal.z = 0;
                info.normal = normalize(info.normal);
                info.normal.z = -info.dis * 10 ;
                info.normal = normalize(info.normal);
                return info; 
            }
            SceneInfo Raymarch(float3 rayPos, float3 rayDir)
            {
                SceneInfo info;
                info.dis = 111;
                info.color = half4(0,0,0,0);
                info.normal = half3(0,0,0);
                for (int i=0; i<_MaxStepValue; ++i)
                {
                    if (_MaxStepValue == 1)
                        info= GetSceneInfo2D(rayPos);
                    else
                        info= GetSceneInfo3D(rayPos);
                    float dis = info.dis;
                    rayPos += rayDir * info.dis;
                    if (dis < _MinDis)
                        break;
                }
                return info;
            }

            v2f vert (uint vertexID : SV_VertexID)
            {
                v2f o;
                o.positionCS = GetFullScreenTriangleVertexPosition(vertexID);
                o.texcoord = GetFullScreenTriangleTexCoord(vertexID);
                return o;
            }

            half4  frag (v2f i) : SV_Target
            {
                float2 uv = i.texcoord;
                float2 ndc = uv * 2 - 1;
                ndc.y = -ndc.y;
                float depth = SampleSceneDepth(i.texcoord);
                float4 H = float4(ndc,depth, 1);
                float4 D = mul(unity_MatrixInvVP, H);
                D = D / D.w;
                float3 depthPos = D.rgb;
                // return half4(D.rgb,1)   ;

                float edgeThrod = 0.6;
                // return half4(unity_OrthoParams.w, 1,1,1);
                float3 rayPos = _WorldSpaceCameraPos;
                float3 rayDir = normalize(depthPos -  rayPos);
                _MaxStepValue = _MaxStep;
                // 判断是否为正交投影
                if (unity_OrthoParams.w > 0.0) // unity_OrthoParams.w 非0表示正交投影
                {
                    // 正交投影：光线方向是固定的（相机正前方）
                    rayDir = normalize(mul((float3x3)unity_CameraToWorld, float3(0,0,-1)));
                    // 光线起点：投影平面上的点（depthPos）
                    rayPos = depthPos;
                    _MaxStepValue = 1;
                    edgeThrod = 0.5;
                }
                SceneInfo info = Raymarch(rayPos, rayDir);
                // return float4(info.normal, 1);
                // return half4(1,1,1,1);
                
                
                if (info.dis > 0)
                    info.color.a = info.color.a / max(1, pow(info.dis * 100, 3));
                if (info.dis > _MinDis * 1000)
                     return 0;
                

                float2 suv = info.normal * 0.5 + 0.5;
                float4 detail = SAMPLE_TEXTURE2D(_DetailTexture, sampler_DetailTexture, suv); 
                float dn = clamp(1-dot(-rayDir, info.normal), 0, 1);
                info.color.a *= dn;
                float maxChannel = max(max(max(info.color.x, info.color.y), info.color.z), 1);
                info.color.rgb = lerp(info.color.rgb,    float3(maxChannel,maxChannel,maxChannel), max(0, dn -edgeThrod) / (1.0-edgeThrod)) + detail.rgb* dn ;
                return info.color ;
            }
            ENDHLSL
        }


        Pass
        {
            Name "SDF2"            
//            ZTest Always
//            ZWrite Off
//            Cull Off
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct appdata
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            }; 
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldPos : TEXCOORD1;
            };

            
            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);

            TEXTURE2D( _SourceTexture);
            SAMPLER(sampler_SourceTexture);

            v2f vert (uint vertexID : SV_VertexID)
            {
                v2f o;
                o.vertex = GetFullScreenTriangleVertexPosition(vertexID);
                o.uv = GetFullScreenTriangleTexCoord(vertexID);
                return o;
            }
            half4  frag (v2f i) : SV_Target
            {
                float4 col1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv) ; 
                float4 col = SAMPLE_TEXTURE2D(_SourceTexture, sampler_SourceTexture, i.uv) ; 
                return float4(col.rgb * (1-col1.a) + col1.rgb * col1.a, 1);
            }
            ENDHLSL
        }

    }
}

